package io.dts.common.util;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.util.Enumeration;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



public class NetUtil {

  private static final Logger logger = LoggerFactory.getLogger(NetUtil.class);
  private static final String LOCALHOST = "127.0.0.1";

  private static final String ANYHOST = "0.0.0.0";

  private static volatile InetAddress LOCAL_ADDRESS = null;

  private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");

  public static String toStringAddress(SocketAddress address) {
    return toStringAddress((InetSocketAddress) address);
  }

  public static String toStringAddress(InetSocketAddress address) {
    return address.getAddress().getHostAddress() + ":" + address.getPort();
  }

  public static InetSocketAddress toInetSocketAddress(String address) {
    int i = address.indexOf(':');
    String host;
    int port;
    if (i > -1) {
      host = address.substring(0, i);
      port = Integer.parseInt(address.substring(i + 1));
    } else {
      host = address;
      port = 0;
    }
    return new InetSocketAddress(host, port);
  }

  public static long toLong(String address) {
    InetSocketAddress ad = toInetSocketAddress(address);
    String[] ip = ad.getAddress().getHostAddress().split("\\.");
    long r = 0;
    r = r | (Long.valueOf(ip[0]) << 40);
    r = r | (Long.valueOf(ip[1]) << 32);
    r = r | (Long.valueOf(ip[2]) << 24);
    r = r | (Long.valueOf(ip[3]) << 16);
    r = r | ad.getPort();
    return r;
  }


  public static String getLocalIp() {
    InetAddress address = getLocalAddress();
    return address == null ? LOCALHOST : address.getHostAddress();
  }

  public static InetAddress getLocalAddress() {
    if (LOCAL_ADDRESS != null)
      return LOCAL_ADDRESS;
    InetAddress localAddress = getLocalAddress0();
    LOCAL_ADDRESS = localAddress;
    return localAddress;
  }

  private static InetAddress getLocalAddress0() {
    InetAddress localAddress = null;
    try {
      localAddress = InetAddress.getLocalHost();
      if (isValidAddress(localAddress)) {
        return localAddress;
      }
    } catch (Throwable e) {
      logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
    }
    try {
      Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
      if (interfaces != null) {
        while (interfaces.hasMoreElements()) {
          try {
            NetworkInterface network = interfaces.nextElement();
            Enumeration<InetAddress> addresses = network.getInetAddresses();
            if (addresses != null) {
              while (addresses.hasMoreElements()) {
                try {
                  InetAddress address = addresses.nextElement();
                  if (isValidAddress(address)) {
                    return address;
                  }
                } catch (Throwable e) {
                  logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
                }
              }
            }
          } catch (Throwable e) {
            logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
          }
        }
      }
    } catch (Throwable e) {
      logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
    }
    logger.error("Could not get local host ip address, will use 127.0.0.1 instead.");
    return localAddress;
  }

  private static boolean isValidAddress(InetAddress address) {
    if (address == null || address.isLoopbackAddress())
      return false;
    String name = address.getHostAddress();
    return (name != null && !ANYHOST.equals(name) && !LOCALHOST.equals(name)
        && IP_PATTERN.matcher(name).matches());
  }

}
